---
layout: layouts/page.njk
title: Tabs
description: A set of layered sections of content—known as tab panels—that are displayed one at a time.
toc:
  - label: Usage
    id: usage
    children:
      - label: HTML + JavaScript
        id: usage-default
        children:
          - label: "Step 1: Include the JavaScript files"
            id: usage-html-js-1
          - label: "Step 2: Add your select HTML"
            id: usage-html-js-2
          - label: HTML structure
            id: usage-html-js-3
          - label: JavaScript events
            id: usage-html-js-4
      - label: Jinja and Nunjucks
        id: usage-macro
---

{% from "macros/code_preview.njk" import code_preview %}
{% from "macros/code_block.njk" import code_block %}
{% from "tabs.njk" import tabs %}

{% set code_html %}
{% set account_panel %}
<div class="card">
  <header>
    <h2>Account</h2>
    <p>Make changes to your account here. Click save when you're done.</p>
  </header>
  <section>
    <form class="form grid gap-6">
      <div class="grid gap-3">
        <label for="demo-tabs-account-name">Name</label>
        <input type="text" id="demo-tabs-account-name" value="Pedro Duarte" />
      </div>
      <div class="grid gap-3">
        <label for="demo-tabs-account-username">Username</label>
        <input type="text" id="demo-tabs-account-username" value="@peduarte" />
      </div>
    </form>
  </section>
  <footer>
    <button type="button" class="btn">Save changes</button>
  </footer>
</div>
{% endset %}
{% set password_panel %}
<div class="card">
  <header>
    <h2>Password</h2>
    <p>Change your password here. After saving, you'll be logged out.</p>
  </header>
  <section>
    <form class="form grid gap-6">
      <div class="grid gap-3">
        <label for="demo-tabs-password-current">Current password</label>
        <input type="password" id="demo-tabs-password-current" />
      </div>
      <div class="grid gap-3">
        <label for="demo-tabs-password-new">New password</label>
        <input type="password" id="demo-tabs-password-new"/>
      </div>
    </form>
  </section>
  <footer>
    <button type="button" class="btn">Save Password</button>
  </footer>
</div>
{% endset %}
{% set tabsets_demo = [
{ tab: "Account", panel: account_panel },
{ tab: "Password", panel: password_panel }
] %}
{{ tabs(
  id='demo-tabs-with-panels',
  tabsets=tabsets_demo,
  main_attrs={ "class": "w-full" },
  tablist_attrs={ "class": "w-full" }
) }}
{% endset %}

{{ code_preview("tabs", code_html | prettyHtml, "max-w-[300px] w-full") }}

<h2 id="usage"><a href="#usage">Usage</a></h2>

<h4 id="usage-html-js-1"><a href="#usage-html-js-1">Step 1: Include the JavaScript files</a></h4>

<section class="prose">
  <p>You can either <a href="/installation/#install-cdn-all">include the JavaScript file for all the components</a>, or just the one for this component by adding this to the <code>&lt;head&gt;</code> of your page:</p>
</section>

{% set code_script %}<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/basecoat.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@{{ pkg.version }}/dist/js/tabs.min.js" defer></script>{% endset %}
{{ code_block(code_script | prettyHtml, "html") }}

<div class="flex flex-wrap gap-2 my-6">
  <a class="badge-outline" href="/installation/#install-js">
    Components with JavaScript
    {% lucide "arrow-right" %}
  </a>
  <a class="badge-outline" href="/installation/#install-cli">
    Use the CLI
    {% lucide "arrow-right" %}
  </a>
  <a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/js/tabs.js" target="_blank">
    tabs.js
    {% lucide "arrow-right" %}
  </a>
</div>

<h4 id="usage-html-js-2"><a href="#usage-html-js-2">Step 2: Add your select HTML</a></h4>

{{ code_block(code_html | prettyHtml, "html") }}

<h4 id="usage-html-js-3"><a href="#usage-html-js-3">HTML structure</a></h4>

<section class="prose">
  <dl>
    <dt><code class="highlight language-html">&lt;div class="tabs"&gt;</code></dt>
    <dd>Wraps around the entire component.
      <dl>
        <dt><code class="highlight language-html">&lt;nav role="tablist" aria-orientation="horizontal"&gt;</code></dt>
        <dd>The tablist containing the tab buttons
          <dl>
            <dt><code class="highlight language-html">&lt;button role="tab" id="{ TAB_ID }" aria-controls="{ PANEL_ID }" aria-selected="false" tabindex="0"&gt;</code></dt>
            <dd>The tab button. When active, the <code>aria-selected</code> attribute is set to <code>true</code>. <code>tabindex="0"</code> is required for keyboard navigation.</dd>
          </dl>
        </dd>
      </dl>
    </dd>
    <dl>
      <dt><code class="highlight language-html">&lt;div role="tabpanel" id="{ PANEL_ID }" aria-labelledby="{ TAB_ID }" tabindex="-1" aria-selected="false"&gt;</code></dt>
      <dd>The tab panel. When active, the <code>aria-selected</code> attribute is set to <code>true</code>.</dd>
    </dl>
  </dl>
</section>

<h4 id="usage-html-js-4"><a href="#usage-html-js-4">JavaScript events</a></h4>

<section class="prose">
  <dl>
    <dt><code>basecoat:initialized</code></dt>
    <dd>Once the component is fully initialized, it dispatches a custom (non-bubbling) <code>basecoat:initialized</code> event on itself.</dd>
  </dl>
</section>

<h3 id="usage-macro"><a href="#usage-macro">Jinja and Nunjucks</a></h3>

<div class="prose">
  <p>You can use the <code class="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs">tabs()</code> Nunjucks or Jinja macro for this component.</p>
</div>

<div class="flex flex-wrap gap-2 my-6">
  <a class="badge-outline" href="/installation/#install-macros" target="_blank">
    Use Nunjucks or Jinja macros
    {% lucide "arrow-right" %}
  </a>
  <a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/jinja/tabs.html.jinja" target="_blank">
    Jinja macro
    {% lucide "arrow-right" %}
  </a>
  <a class="badge-outline" href="https://github.com/hunvreus/basecoat/blob/main/src/nunjucks/tabs.njk" target="_blank">
    Nunjucks macro
    {% lucide "arrow-right" %}
  </a>
</div>

{% set raw_code %}{% raw %}{% set account_panel %}
<div class="card">
  <header>
    <h2>Account</h2>
    <p>Make changes to your account here. Click save when you're done.</p>
  </header>
  <section>
    <form class="form grid gap-6">
      <div class="grid gap-3">
        <label for="demo-tabs-account-name">Name</label>
        <input type="text" id="demo-tabs-account-name" value="Pedro Duarte" />
      </div>
      <div class="grid gap-3">
        <label for="demo-tabs-account-username">Username</label>
        <input type="text" id="demo-tabs-account-username" value="@peduarte" />
      </div>
    </form>
  </section>
  <footer>
    <button type="button" class="btn">Save changes</button>
  </footer>
</div>
{% endset %}

{% set password_panel %}
<div class="card">
  <header>
    <h2>Password</h2>
    <p>Change your password here. After saving, you'll be logged out.</p>
  </header>
  <section>
    <form class="form grid gap-6">
      <div class="grid gap-3">
        <label for="demo-tabs-password-current">Current password</label>
        <input type="password" id="demo-tabs-password-current" />
      </div>
      <div class="grid gap-3">
        <label for="demo-tabs-password-new">New password</label>
        <input type="password" id="demo-tabs-password-new"/>
      </div>
    </form>
  </section>
  <footer>
    <button type="button" class="btn">Save Password</button>
  </footer>
</div>
{% endset %}

{% set tabsets_demo = [
{ tab: "Account", panel: account_panel },
{ tab: "Password", panel: password_panel }
] %}

{{ tabs(
  id='demo-tabs-with-panels',
  tabsets=tabsets_demo,
  main_attrs={ "class": "w-full" },
  tablist_attrs={ "class": "w-full" }
) }}{% endraw %}{% endset%}
{{ code_block(raw_code, "jinja") }}